我这里有谷歌给OEM提供的官方文档,需要的可以联系
什么是PAI
PAI (PlayAutoInstall) 是一个自动下载安装APK到手机,并且摆放在Launcher对应位置的一个机制。
因为国内没有GMS,所以很多人没接触过这个机制。这个机制其实对于运营商定制来说非常重要,比如美国的运营商,一个运营商有很多地区很多种类的SIM卡,当插上不同地区的SIM卡,运营商定制的手机就会下载不同的APP摆放在界面不同的位置。
这么说有些抽象,看下面示例图:
首先是出厂刚开机Launcher的界面:
当手机插上SIM卡联网,PlayStore会通过PAI机制下载安装摆放不同的APP:
界面一下子就变丰富了。
PAI 流程
本地编译一个PlayAutoInstallConfig.apk,签名上传到APFE服务器,APFE会验证配置信息,并提供给PlayStore。当目标设备第一次开机启动并且联网(现在不必要登录谷歌帐号),这些应用就会加入下载队列,自动下载到手机。
配置菜单
先聊一下APFE会验证的配置信息。
需要的配置信息包括:
- 指纹(必须)
- 城市(可选)
- 运营商(可选)
- 需要下载的App列表
- 应用在桌面的位置信息
后两项是编译在PlayAutoInstallConfig.apk中的,前三项是把apk上传到服务器时需要填写的。
上传服务器配置页面如下:
配置信息的前三项匹配项如果填写,就必须要完全匹配才能应用到手机。我遇到一个问题是配置上传后PlayAutoInstallConfig.apk会在SetupWizard过程中下载到手机,但需要PlayStore下载的应用怎么都不下载。后来发现是在上传apk到服务器时运营商填的不对,导致无法下载。因为尝试填写几种运营商Name都不能正常工作,最后解决方案是只匹配指纹,不匹配城市和运营商(减少过滤项),这样手机就可以和PlayStore信息匹配,然后就可以自动下载了。
关于其余配置,参考下面表格:
Config APK
对上表中出现一个Config APK 做一些解释。
手机内必须要先预置一个符合下列条件的stub APK:
- 为一个系列的设备设置唯一的包名,包名格式为android.autoinstalls.config.
. - 必须配置一个receiver ”android.autoinstalls.config.action.PLAY_AUTO_INSTALL”,并且设置exported为flase
- 在预置的App里只能有一个定义这个receiver
- versionCode必须定义成1
- APK必须预置在/system/app (不能定义成privileged,即不能放/priv-app)
- 必须用私有Key签名
- 不能定义permissions/activities/其他receivers/content provider/services
这个APK其实就一个Manifest,没有特别的内容。
相关的MakeFile如下
include $(CLEAR_VARS)
apk_variant := stub
APK_VERSION := 1
include $(BUILD_APK)
PlayAutoInstallConfig.apk
这个APK是我们真正配置的APK。
它和前面的APK的关系是:包名一致。因为PAI机制需要本地存在一个这个包名的APK,在开机的SetupWizard阶段,(如果联网)它会从服务器下载这个写有对应配置的APK到手机上,替换掉那个Stub APK。
关于PAIconfig APK的配置:
- 上传的APK(也就是我们编出来的APK)包名与指纹要和stub一致
- APK签名要一致
- 和stub配置同样的receiver
- versionCode必须大于1000
- APK必须包含launcher布局配置的xml文件(即后面会提到的default_layout),不然上传会失败,因为上传前会检查这个xml文件,然后会把要下载的app显示出来。所以也必须要求至少定义一个需要下载的app。最多50个,建议放10~15个。(文档还要求autoinstall的应用必须在launcher上指定摆放位置,目前看来是不需要的,有可能bb launcher做了修改)
- 界面会有文件夹,文件夹名称string在APK本地resource定义,支持国际化。
- 需要自动下载的APK对设备来讲必须是在PlayStore发布的,并且对该地区用户可见
- 不能定义permissions/activities/其他receivers/content provider/services
在这个APK里就要写需要下载的APK信息了,基本格式如下:
参数screen是第几屏,x,y是在屏幕的位置,从0开始。
autoinstall开头是需要PlayStore下载的,
appwidget开头是桌面控件。span属性设定控件的宽高
appicon就是纯粹摆放APP的位置
folder是文件夹,里面可以放多个app
action是shortcut快捷键,key是LAUNCHER里面定义的
(按顺序?或者按照rank属性决定)最后四个是DOCKER的app icon
代码结构
在res-PAI目录中,xml有两个,default_layout中要填写
验证流程
烧一个新手机,启动进SetupWizard,在过程中联网,登录谷歌帐号(现在这一步不必要了)。在设置好谷歌帐号之后,就会自动下载PAI apk到手机,替换默认的stub生效。等到进入Launcher界面,Launcher就会加载这个apk,把icon配置到对应位置上。与此同时,PlayStore会下载配置的App。
从工作流程看,如果想调试界面,就可以不reset手机,直接把编译好的APK替换默认的stub。这时候因为launcher已经启动过,所以不会主动加载配置的apk,需要从settings清空launcher数据,强制加载,就可以看到修改的界面了。
更多
Launcher里面有AutoInstallsLayout.java ,可以阅读了解。
从上面配置可以看出,写配置需要了解应用的包名,主Activity的类名,所以需要用到am和pm命令去查找,所以写了一个脚本方便获取APK的信息。
#!/bin/bash
help_info(){
echo "-a get current activity"
echo "-p list packages in device, can add a package name"
echo "-m dump apk's AndroidManifest, need add a APK"
}
if [ $# -eq 0 ]
then
help_info
else
echo "arg count is $#"
fi
case "$1" in
-a)
adb shell dumpsys activity activities
;;
-p)
if [ $# -eq 2 ]
then
adb shell pm list packages -f "$2"
elif [ $# -eq 1 ]
then
adb shell pm list packages
else
echo "check the arguement"
fi
;;
-m)
if [ $# -eq 2 ]
then
aapt dump xmltree "$2" AndroidManifest.xml
else
echo "need file path"
fi
esac